#include "xcrypt.h"
#include <cstring>
#include <iostream>

bool XCrypt::init(const std::string &password)
{
    const_DES_cblock key = {0};
    size_t keySize = password.size();
    if (keySize > sizeof(key)) {
        keySize = sizeof(key);
    }

    memcpy(key, password.c_str(), keySize);
    DES_set_key(&key, &keySch_);
    return true;
}

int XCrypt::encypt(const char *inData, int inSize, char *outData, bool isEnd)
{
    int ret;
    int writeBytes;
    ret = 0;
    do {
        if (!inData || inSize == 0 || !outData) {
            ret = -1;
            break;
        }
        const_DES_cblock in = { 0 };
        DES_cblock out = { 0 };
        writeBytes = 0;
        const int blockSize = sizeof(const_DES_cblock);
        int dataSize = 0;
        int padding = blockSize - inSize % blockSize;
        for (int i = 0; i < inSize; i += blockSize) {
            dataSize = (inSize - i) < blockSize ? inSize - i : blockSize;
            memcpy(in, inData + writeBytes, dataSize);
            if (isEnd && (i + blockSize) >= inSize) {
                if (padding == blockSize) {
                    DES_ecb_encrypt(&in, &out, &keySch_, DES_ENCRYPT);
                    memcpy(outData + writeBytes, &out, blockSize);
                    writeBytes += blockSize;
                    memset(in, padding, sizeof(in));
                } else {
                    memset(in + inSize % blockSize, padding, padding);
                }
            }
            DES_ecb_encrypt(&in, &out, &keySch_, DES_ENCRYPT);
            memcpy(outData + writeBytes, &out, blockSize);
            writeBytes += blockSize;
        }
        ret = writeBytes;
    } while (0);

    return ret;
}

int XCrypt::Dencypt(const char *inData, int inSize, char *outData, bool isEnd)
{
   int ret;
    int writeBytes;
    ret = 0;
    do {
        if (!inData || inSize == 0 || !outData) {
            ret = -1;
            break;
        }
        const_DES_cblock in = { 0 };
        DES_cblock out = { 0 };
        writeBytes = 0;
        const int blockSize = sizeof(const_DES_cblock);
        int dataSize = 0;
        for (int i = 0; i < inSize; i += blockSize) {
            memcpy(in, inData + writeBytes, blockSize);
            DES_ecb_encrypt(&in, &out, &keySch_, DES_DECRYPT);
            dataSize = blockSize;
            if (isEnd && inSize - i <= blockSize) {
                dataSize = blockSize - out[7];
                if (dataSize == 0) {
                    break;
                } else if (dataSize < 0) {
                    std::cerr << "Decrypt failed padding size err" << std::endl;
                    break;
                }
            }
            memcpy(outData + writeBytes, &out, dataSize);
            writeBytes += dataSize;
        }
        ret = writeBytes;
    } while (0);

    return ret;
}

int XCrypt::getPadding(int dataSize)
{
    const int blockSize = sizeof(const_DES_cblock);
    int padding = blockSize - dataSize % blockSize;
    return padding == 0 ? blockSize : padding;
}